package com.foodev.maven.gwt;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

public abstract class AbstractGwtScriptMojo extends AbstractGwtMojo {

    /**
     * The 'style' parameter, should be one of <code>OBF[USCATED]</code>, <code>PRETTY</code> or <code>DETAILED</code>.
     * (Defaults to <code>OBF</code>).
     * 
     * @parameter
     */
    protected String style = null;

    /**
     * The maximum amount of heap space to use, default is '256m' (-Xmx256m).
     * 
     * @parameter expression="256m"
     */
    protected String maxmem;

    /**
     * The maximun amount of stack to use, default is '256k' (-Xss256k)
     * 
     * @parameter expression="256k"
     */
    protected String maxstack;

    /**
     * Debugging: causes the compiled output to check assert statements.
     * 
     * @parameter expression=false
     */
    protected boolean ea;

    /**
     * Specifies the number of local workers to use when compiling permutations. Defaults to one.
     * 
     * @parameter expression=1
     */
    protected int localWorkers;

    /**
     * The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG, SPAM, or ALL. Defaults to INFO.
     * <p>
     * Additionally if this plugin is involved with -X and there is no explicit logLevel configuration, the log level
     * will be set to DEBUG
     * </p>
     * 
     * @parameter
     */
    protected String logLevel;

    /**
     * Turn on the JVM debug flags, making it possible to attach an external debugger.
     * 
     * @parameter expression="${xgwt.debug}" default-value="false"
     */
    protected boolean debug;

    /**
     * When running in debug mode, this parameter controls which port to use for remote debugging. The default is 1044.
     * 
     * @parameter expression="${xgwt.debugPort}" default-value="1044"
     */
    protected int debugPort;


    /**
     * Creates a new executable script under the <code>gwtHome</code>.
     * 
     * @param name
     *            the name of the script without the ending .sh or .bat which will be added depending on OS.
     * @return the newly created file.
     * @throws IOException
     */
    protected File createScriptFile(String name) throws IOException {

        if (!gwtHome.exists())
            gwtHome.mkdirs();

        File f = new File(gwtHome, name + (isWindows() ? ".bat" : ".sh"));

        if (f.exists())
            f.delete();

        f.createNewFile();

        if (isWindows()) {
            // no +x in windows
        } else {

            try {
                ProcessBuilder pb = new ProcessBuilder(new String[] { "chmod", "a+x", f.getAbsolutePath() });
                Process proc = pb.start();
                proc.waitFor();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }

        return f;

    }


    /**
     * Writes the script header which for unix is <code>#!/bin/sh</code> or <code>#!/usr/bin/sh</code> and nothing for
     * windows.
     * 
     * @param writer
     *            the writer to write to
     * @throws IOException
     */
    protected void startScript(PrintWriter writer) throws IOException {

        if (isWindows()) {

            if (!getLog().isDebugEnabled())
                writer.println("@ECHO OFF");

        } else {

            File sh = new File("/bin/sh");
            if (!sh.exists()) {
                sh = new File("/usr/bin/sh");
            }

            writer.println("#!" + sh.getAbsolutePath());

            if (getLog().isDebugEnabled())
                writer.println("set -x");

        }

    }


    /**
     * Writes the given list of strings as a classpath to the given writer.
     * 
     * @param writer
     *            The writer to write to
     * @param classpath
     *            The classpath as elements in a list of strings.
     * @throws IOException
     */
    protected void writeClasspath(PrintWriter writer, List<String> classpath) throws IOException {

        StringBuilder buf = new StringBuilder();

        if (isWindows()) {
            buf.append("set CLASSPATH=");
        } else {
            buf.append("export CLASSPATH=");
        }
        for (Iterator<String> iter = classpath.iterator(); iter.hasNext();) {
            buf.append("\"" + iter.next() + "\"" + File.pathSeparator);
            // break if line is longer than 4000
            if (buf.length() > 4000 && iter.hasNext()) {
                buf.deleteCharAt(buf.length() - 1); // last ':'
                writer.println(buf.toString());
                buf = new StringBuilder();
                if (isWindows()) {
                    buf.append("set CLASSPATH=%CLASSPATH%;");
                } else {
                    buf.append("CLASSPATH=\"$CLASSPATH\":");
                }
            }
        }
        buf.deleteCharAt(buf.length() - 1); // last ':'
        writer.println(buf.toString());

    }


    protected void writeJavaCommand(PrintWriter writer) throws IOException {

        String javaHome = System.getenv("JAVA_HOME");

        if (javaHome != null) {

            writer.write("\"" + javaHome + File.separator + "bin" + File.separator + "java" + "\"");

        } else {

            writer.write("java");

        }

    }


    /**
     * Outputs subclass shared VM arguments.
     * 
     * @param writer
     *            The writer to write to.
     * @throws IOException
     *             If something goes wrong when writing.
     */
    protected void writeSharedVMArgs(PrintWriter writer) throws IOException {

        if (maxmem != null) {
            writer.write(" -Xmx" + maxmem);
        }
        if (maxstack != null) {
            writer.write(" -Xss" + maxstack);
        }

        if (debug) {
            writer.write(" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + debugPort);
        }

        if (isWindows()) {
            writer.write(" -cp %CLASSPATH%");
        }

    }


    /**
     * Outputs arguments shared between Compiler and HostedMode.
     * 
     * @param writer
     *            The writer to write to.
     * @param output
     *            the output directory that the -war argument will point to. Default for compilation is set by
     *            {@link AbstractGwtMojo#webappSrc} whilst for hosted mode it is {@link ShellMojo#hostedModeWar}
     * @throws IOException
     *             If something goes wrong when writing.
     */
    protected void writeSharedArgs(PrintWriter writer, File output) throws IOException {

        if (logLevel != null) {
            writer.write(" -logLevel " + logLevel);
        } else if (getLog().isDebugEnabled()) {
            writer.write(" -logLevel DEBUG");
        }

        if (style != null) {
            writer.write(" -style " + style);
        }

        writer.write(" -gen \"" + new File(gwtHome, "gen").getAbsolutePath() + "\"");

        writer.write(" -extra \"" + new File(gwtHome, "extra").getAbsolutePath() + "\"");

        writer.write(" -workDir \"" + new File(gwtHome, "workDir").getAbsolutePath() + "\"");

        writer.write(" -war \"" + output.getAbsolutePath() + "\"");

        if (ea)
            writer.write(" -ea");

        if (localWorkers != 1)
            writer.write(" -localWorkers " + localWorkers);

    }


    /**
     * Writes the modules one by one.
     * 
     * @param writer
     *            The writer to write to.
     * @throws IOException
     *             If something goes wrong when writing.
     */
    protected void writeModules(PrintWriter writer) throws IOException {

        for (String module : modules) {
            writer.write(" " + module);
        }

    }

}
